#include "stdafx.h"
#include "log.h"

#include <pimstore.h>

HANDLE Logger::hFile = INVALID_HANDLE_VALUE;
TCHAR  Logger::buf[2048] = {0};
DWORD  Logger::severity = lsDefault;

struct SPropMapItem
{
  CEPROPID prop;
  TCHAR    name[64];
};

#define MAKEPROPMAPITEM(x) { x, L#x },

SPropMapItem PropMap[] = {
      MAKEPROPMAPITEM( PIMPR_INVALID_ID )
      MAKEPROPMAPITEM( PIMPR_ALL_PHONE )
      MAKEPROPMAPITEM( PIMPR_ALL_EMAIL )
      MAKEPROPMAPITEM( PIMPR_ALL_TEXT_MESSAGING )
      MAKEPROPMAPITEM( PIMPR_ALL_INSTANT_MESSAGING )
      MAKEPROPMAPITEM( PIMPR_ALL_COMMUNICATION_METHODS )
      MAKEPROPMAPITEM( PIMPR_ALL_VOICE )
      MAKEPROPMAPITEM( PIMPR_ALL_PHONE_AND_SIM )
      MAKEPROPMAPITEM( PIMPR_OID )
      MAKEPROPMAPITEM( PIMPR_FOLDERNOTIFICATIONS )
      MAKEPROPMAPITEM( PIMPR_FOLDER_CATEGORIES )
      MAKEPROPMAPITEM( PIMPR_SOURCE_ID )
      MAKEPROPMAPITEM( PIMPR_RECENT )
      MAKEPROPMAPITEM( PIMPR_CATEGORIES )
      MAKEPROPMAPITEM( PIMPR_BODY_BINARY )
      MAKEPROPMAPITEM( PIMPR_BODY_TEXT )
      MAKEPROPMAPITEM( PIMPR_SUBJECT )
      MAKEPROPMAPITEM( PIMPR_SENSITIVITY )
      MAKEPROPMAPITEM( PIMPR_IS_RECURRING )
      MAKEPROPMAPITEM( PIMPR_REMINDER_SET )
      MAKEPROPMAPITEM( PIMPR_REMINDER_SOUND_FILE )
      MAKEPROPMAPITEM( PIMPR_REMINDER_OPTIONS )
      MAKEPROPMAPITEM( PIMPR_RECURRING_TYPE )
      MAKEPROPMAPITEM( PIMPR_RECURRING_PATTERNSTARTDATE )
      MAKEPROPMAPITEM( PIMPR_RECURRING_PATTERNENDDATE )
      MAKEPROPMAPITEM( PIMPR_RECURRING_STARTTIME )
      MAKEPROPMAPITEM( PIMPR_RECURRING_ENDTIME )
      MAKEPROPMAPITEM( PIMPR_RECURRING_NOEND )
      MAKEPROPMAPITEM( PIMPR_RECURRING_OCCURRENCES )
      MAKEPROPMAPITEM( PIMPR_RECURRING_INTERVAL )
      MAKEPROPMAPITEM( PIMPR_RECURRING_DAYOFWEEKMASK )
      MAKEPROPMAPITEM( PIMPR_RECURRING_DAYOFMONTH )
      MAKEPROPMAPITEM( PIMPR_RECURRING_INSTANCE )
      MAKEPROPMAPITEM( PIMPR_RECURRING_DURATION )
      MAKEPROPMAPITEM( PIMPR_RECURRING_MONTHOFYEAR )
      MAKEPROPMAPITEM( PIMPR_DURATION )
      MAKEPROPMAPITEM( PIMPR_LOCATION )
      MAKEPROPMAPITEM( PIMPR_START )
      MAKEPROPMAPITEM( PIMPR_END )
      MAKEPROPMAPITEM( PIMPR_ALL_DAY_EVENT )
      MAKEPROPMAPITEM( PIMPR_BUSY_STATUS )
      MAKEPROPMAPITEM( PIMPR_REMINDER_MINUTES_BEFORE_START )
      MAKEPROPMAPITEM( PIMPR_GLOBAL_OBJECT_ID )
      MAKEPROPMAPITEM( PIMPR_TIMEZONE )
      MAKEPROPMAPITEM( PIMPR_MEETING_STATUS )
      MAKEPROPMAPITEM( PIMPR_MEETING_ORGANIZER_NAME )
      MAKEPROPMAPITEM( PIMPR_MEETING_OWNER_CRITICAL_CHANGE )
      MAKEPROPMAPITEM( PIMPR_ATTENDEES_CRITICAL_CHANGE )
      MAKEPROPMAPITEM( PIMPR_IMPORTANCE )
      MAKEPROPMAPITEM( PIMPR_TEAM_TASK )
      MAKEPROPMAPITEM( PIMPR_START_DATE )
      MAKEPROPMAPITEM( PIMPR_DUE_DATE )
      MAKEPROPMAPITEM( PIMPR_DATE_COMPLETED )
      MAKEPROPMAPITEM( PIMPR_COMPLETE )
      MAKEPROPMAPITEM( PIMPR_REMINDER_TIME )
      MAKEPROPMAPITEM( PIMPR_RECURRING_REGENERATING )
      MAKEPROPMAPITEM( PIMPR_FILEAS )
      MAKEPROPMAPITEM( PIMPR_TITLE )
      MAKEPROPMAPITEM( PIMPR_FIRST_NAME )
      MAKEPROPMAPITEM( PIMPR_MIDDLE_NAME )
      MAKEPROPMAPITEM( PIMPR_LAST_NAME )
      MAKEPROPMAPITEM( PIMPR_SUFFIX )
      MAKEPROPMAPITEM( PIMPR_NICKNAME )
      MAKEPROPMAPITEM( PIMPR_YOMI_FIRSTNAME )
      MAKEPROPMAPITEM( PIMPR_YOMI_LASTNAME )
      MAKEPROPMAPITEM( PIMPR_YOMI_COMPANY )
      MAKEPROPMAPITEM( PIMPR_COMPANY_NAME )
      MAKEPROPMAPITEM( PIMPR_DEPARTMENT )
      MAKEPROPMAPITEM( PIMPR_JOB_TITLE )
      MAKEPROPMAPITEM( PIMPR_MANAGER )
      MAKEPROPMAPITEM( PIMPR_OFFICE_LOCATION )
      MAKEPROPMAPITEM( PIMPR_ASSISTANT_NAME )
      MAKEPROPMAPITEM( PIMPR_EMAIL1_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_EMAIL2_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_EMAIL3_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_IM1_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_IM2_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_IM3_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_MOBILE_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_BUSINESS2_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_HOME_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_HOME2_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_FAX_NUMBER )
      MAKEPROPMAPITEM( PIMPR_HOME_FAX_NUMBER )
      MAKEPROPMAPITEM( PIMPR_PAGER_NUMBER )
      MAKEPROPMAPITEM( PIMPR_CAR_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_RADIO_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_COMPANY_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_ASSISTANT_TELEPHONE_NUMBER )
      MAKEPROPMAPITEM( PIMPR_SMS )
      MAKEPROPMAPITEM( PIMPR_MMS )
      MAKEPROPMAPITEM( PIMPR_DISPLAY_NAME )
      MAKEPROPMAPITEM( PIMPR_SPOUSE )
      MAKEPROPMAPITEM( PIMPR_CHILDREN )
      MAKEPROPMAPITEM( PIMPR_WEB_PAGE )
      MAKEPROPMAPITEM( PIMPR_RINGTONE )
      MAKEPROPMAPITEM( PIMPR_CUSTOMERID )
      MAKEPROPMAPITEM( PIMPR_GOVERNMENTID )
      MAKEPROPMAPITEM( PIMPR_ACCOUNT_NAME )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_ADDRESS_STREET )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_ADDRESS_CITY )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_ADDRESS_STATE )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_ADDRESS_POSTAL_CODE )
      MAKEPROPMAPITEM( PIMPR_BUSINESS_ADDRESS_COUNTRY )
      MAKEPROPMAPITEM( PIMPR_HOME_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_HOME_ADDRESS_STREET )
      MAKEPROPMAPITEM( PIMPR_HOME_ADDRESS_CITY )
      MAKEPROPMAPITEM( PIMPR_HOME_ADDRESS_STATE )
      MAKEPROPMAPITEM( PIMPR_HOME_ADDRESS_POSTAL_CODE )
      MAKEPROPMAPITEM( PIMPR_HOME_ADDRESS_COUNTRY )
      MAKEPROPMAPITEM( PIMPR_OTHER_ADDRESS )
      MAKEPROPMAPITEM( PIMPR_OTHER_ADDRESS_STREET )
      MAKEPROPMAPITEM( PIMPR_OTHER_ADDRESS_CITY )
      MAKEPROPMAPITEM( PIMPR_OTHER_ADDRESS_STATE )
      MAKEPROPMAPITEM( PIMPR_OTHER_ADDRESS_POSTAL_CODE )
      MAKEPROPMAPITEM( PIMPR_OTHER_ADDRESS_COUNTRY )
      MAKEPROPMAPITEM( PIMPR_BIRTHDAY )
      MAKEPROPMAPITEM( PIMPR_ANNIVERSARY )
      MAKEPROPMAPITEM( PIMPR_SMARTPROP )
      MAKEPROPMAPITEM( PIMPR_PICTURE )
      MAKEPROPMAPITEM( PIMPR_YOMI_FILEAS )
      MAKEPROPMAPITEM( PIMPR_SIM_PHONE )
      { 0, 0 }
};

void Logger::BeginLog(TCHAR* file, DWORD sev)
{
#ifdef LOG_ENABLED
  EndLog();

  severity = sev;

  TCHAR szPath[MAX_PATH];
  ::GetModuleFileName(NULL, szPath, MAX_PATH);
  TCHAR * pstr = _tcsrchr(szPath, TEXT('\\'));
  if (pstr)
    *(++pstr) = 0;
  _tcscat(szPath, file);

  hFile = CreateFile(szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL);

  // unicode header
  BYTE bom[] = { 0xEF, 0xBB, 0xBF };
  DWORD num;
  WriteFile(hFile, (LPCVOID)bom, sizeof(bom), &num, NULL);

  TCHAR date[50];
  TCHAR time[50];
  GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, date, 50);
  GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, time, 50);
  LogFmt(lsSystem, TEXT("------ Log Started %s %s"), date, time);
#endif
}

void Logger::EndLog()
{
#ifdef LOG_ENABLED
  if (hFile != INVALID_HANDLE_VALUE)
  {
    TCHAR date[50];
    TCHAR time[50];
    GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, date, 50);
    GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, time, 50);
    LogFmt(lsSystem, TEXT("------ Log Finished %s %s"), date, time);

    CloseHandle(hFile);
    hFile = INVALID_HANDLE_VALUE;
  }
#endif
}

void Logger::SetSeverity(DWORD sev)
{
#ifdef LOG_ENABLED
  severity = sev;
#endif
}

void Logger::Log(LogSeverity sev, const TCHAR* d)
{
#ifdef LOG_ENABLED
  if (!(severity & sev))
    return;

  if (hFile != INVALID_HANDLE_VALUE)
  {
    DWORD num;
    int bufSize = WideCharToMultiByte(CP_UTF8, 0, d, _tcslen(d), 0, 0, 0, 0);
    if (bufSize <= 0)
      return;
    char* pBuf = (char*)malloc(bufSize);
    if (!pBuf)
      return;
    WideCharToMultiByte(CP_UTF8, 0, d, _tcslen(d), pBuf, bufSize, 0, 0);
    WriteFile(hFile, (LPCVOID)pBuf, bufSize, &num, NULL);
    free(pBuf);
    WriteFile(hFile, (LPCVOID)"\n", sizeof(char), &num, NULL);
  }
#endif
}

void Logger::Log(LogSeverity sev, int d)
{
#ifdef LOG_ENABLED
  if (!(severity & sev))
    return;

  _itow(d, buf, 10);
  Log(sev, buf);
#endif
}

void Logger::Log(LogSeverity sev, long d)
{
#ifdef LOG_ENABLED
  if (!(severity & sev))
    return;

  _ltow(d, buf, 10);
  Log(sev, buf);
#endif
}

void Logger::LogFmt(LogSeverity sev, const TCHAR* fmt, ...)
{
#ifdef LOG_ENABLED
  if (!(severity & sev))
    return;

  va_list args;
  va_start(args, fmt);
  vswprintf(buf, fmt, args);
  va_end(args);
  Log(sev, buf);
#endif
}

void Logger::LogCePropVal(LogSeverity sev, const TCHAR* label, CEPROPVAL& prop)
{
#ifdef LOG_ENABLED
  if (!(severity & sev))
    return;

  TCHAR sid[12];
  _itot(HIWORD(prop.propid), sid, 10);
  TCHAR type[64] = {0};
  TCHAR val[1501] = {0};
  TCHAR sflags[64] = {0};
  if (prop.wFlags & CEDB_PROPNOTFOUND)
    _tcscat(sflags, TEXT("NOT FOUND"));

  switch(LOWORD(prop.propid))
  {
  case CEVT_BLOB:
    wsprintf(type, TEXT("BLOB (%d)"), LOWORD(prop.propid));
    wsprintf(val, TEXT("-- not supported --"));
    break;
  case CEVT_BOOL:
    wsprintf(type, TEXT("BOOL (%d)"), LOWORD(prop.propid));
    wsprintf(val, TEXT("%s"), prop.val.boolVal ? TEXT("True") : TEXT("False"));
    break;
  case CEVT_FILETIME:
    wsprintf(type, TEXT("FILETIME (%d)"), LOWORD(prop.propid));
    {
      SYSTEMTIME st;
      if (FileTimeToSystemTime(&prop.val.filetime, &st))
      {
        ::GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, val, 1500);
      }
    }
    break;
  case CEVT_I2:
    wsprintf(type, TEXT("I2 (%d)"), LOWORD(prop.propid));
    wsprintf(val, TEXT("%d"), (int)prop.val.iVal);
    break;
  case CEVT_I4:
    wsprintf(type, TEXT("I4 (%d)"), LOWORD(prop.propid));
    wsprintf(val, TEXT("%ld"), (long)prop.val.lVal);
    break;
  case CEVT_R8:
    wsprintf(type, TEXT("R8 (%d)"), LOWORD(prop.propid));
    wsprintf(val, TEXT("%lf"), prop.val.dblVal);
    break;
  case CEVT_UI2:
    wsprintf(type, TEXT("UI2 (%d)"), LOWORD(prop.propid));
    wsprintf(val, TEXT("%u"), prop.val.uiVal);
    break;
  case CEVT_UI4:
    wsprintf(type, TEXT("UI4 (%d)"), LOWORD(prop.propid));
    wsprintf(val, TEXT("%lu"), prop.val.ulVal);
    break;
  case CEVT_LPWSTR:
    wsprintf(type, TEXT("LPWSTR (%d)"), LOWORD(prop.propid));
    _tcscpy(val, prop.val.lpwstr ? prop.val.lpwstr : TEXT("NULL"));
    break;
  default:
    wsprintf(type, TEXT("UNSUPPORTED (%d)"), LOWORD(prop.propid));
    break;
  }
  wsprintf(buf, TEXT("%s %s: id='%s', type='%s', flags='%s', value='%s'"), label, GetCePropName(prop.propid), sid, type, sflags, val);
  Log(sev, buf);
#endif
}

const TCHAR* Logger::GetCePropName(CEPROPID propId)
{
#ifdef LOG_ENABLED
  for(int i = 0;; i++)
  {
    if (PropMap[i].prop == 0)
      break;
    if (PropMap[i].prop == propId)
      return PropMap[i].name;
  }
#endif
  return TEXT("UNKNOWN");
}

void Logger::LogTimestamp(LogSeverity sev)
{
#ifdef LOG_ENABLED
  if (!(severity & sev))
    return;

  SYSTEMTIME st;
  ::GetLocalTime(&st);
  LogFmt(sev, TEXT("%0d:%0d:%0d:%03d: "), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
#endif
}

void Logger::LogLastError(LogSeverity sev)
{
#ifdef LOG_ENABLED
  if (!(severity & sev))
    return;

  DWORD dwErr = GetLastError();
  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, buf, 2047, 0);
  Log(sev, buf);
#endif
}